/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/**
 * @author Ji Qi, 2006/10/05
 */

#ifndef _LSPACE_H_
#define _LSPACE_H_

#include "../common/gc_common.h"
#include "../thread/gc_thread.h"
#include "free_area_pool.h"
#ifdef USE_32BITS_HASHCODE
#include "../common/hashcode.h"
#endif

/*Fixme: This macro is for handling HEAP_BASE issues caused by JIT OPT*/
#ifdef COMPRESS_REFERENCE
  #define LOS_HEAD_RESERVE_FOR_HEAP_BASE ( SPACE_ALLOC_UNIT )
#else
  #define LOS_HEAD_RESERVE_FOR_HEAP_BASE ( 0*KB )
#endif

typedef struct Lspace{
  /* <-- first couple of fields are overloadded as Space */
  void* heap_start;
  void* heap_end;
  POINTER_SIZE_INT reserved_heap_size;
  POINTER_SIZE_INT committed_heap_size;
  unsigned int num_collections;
  int64 time_collections;
  float survive_ratio;
  unsigned int collect_algorithm;  
  GC* gc;
  /*LOS_Shrink:This field stands for sliding compact to lspace */
  Boolean move_object;

  Space_Statistics* space_statistic; 

  /* Size allocted since last collection. */
  volatile uint64 last_alloced_size;
  /* Size allocted since last major collection. */
  uint64 accumu_alloced_size;
  /* Total size allocated since VM starts. */
  uint64 total_alloced_size;

  /* Size survived from last collection. */
  uint64 last_surviving_size;
  /* Size survived after a certain period. */
  uint64 period_surviving_size;  
  /* END of Space --> */

  Free_Area_Pool* free_pool;
  /*Size of allocation which caused lspace alloc failure.
   *This one is used to assign area to failed collection inside gc.
   *Resetted in every gc_assign_free_area_to_mutators
   */
  POINTER_SIZE_INT failure_size;
  void* success_ptr;
  
  void* scompact_fa_start;
  void* scompact_fa_end;
}Lspace;

Lspace *lspace_initialize(GC* gc, void* reserved_base, POINTER_SIZE_INT lspace_size);
void lspace_destruct(Lspace* lspace);
Managed_Object_Handle lspace_alloc(unsigned size, Allocator* allocator);
void* lspace_try_alloc(Lspace* lspace, POINTER_SIZE_INT alloc_size);
void lspace_sliding_compact(Collector* collector, Lspace* lspace);
void lspace_compute_object_target(Collector* collector, Lspace* lspace);
void lspace_reset_for_slide(Lspace* lspace);
void lspace_collection(Lspace* lspace);

inline POINTER_SIZE_INT lspace_free_memory_size(Lspace* lspace)
{
  if(!lspace) return 0;
  /* FIXME:: */
  assert(lspace->committed_heap_size >= (POINTER_SIZE_INT)lspace->last_surviving_size + (POINTER_SIZE_INT)lspace->last_alloced_size);
  return (lspace->committed_heap_size - (POINTER_SIZE_INT)lspace->last_surviving_size - (POINTER_SIZE_INT)lspace->last_alloced_size);
}

inline POINTER_SIZE_INT lspace_committed_size(Lspace* lspace)
{
  if(lspace)
    return lspace->committed_heap_size;
  else
    return 0;
}

inline Partial_Reveal_Object* lspace_get_next_marked_object( Lspace* lspace, unsigned int* iterate_index)
{
    POINTER_SIZE_INT next_area_start = (POINTER_SIZE_INT)lspace->heap_start + (*iterate_index) * KB;
    BOOLEAN reach_heap_end = 0;
    unsigned int hash_extend_size = 0;

    while(!reach_heap_end){
        //FIXME: This while shoudl be if, try it!
        while((next_area_start< (POINTER_SIZE_INT)lspace->heap_end)&&!*((POINTER_SIZE_INT*)next_area_start)){
            assert(((Free_Area*)next_area_start)->size);
            next_area_start += ((Free_Area*)next_area_start)->size;            
        }
        if(next_area_start < (POINTER_SIZE_INT)lspace->heap_end){
            //If there is a living object at this addr, return it, and update iterate_index

#ifdef USE_32BITS_HASHCODE
            hash_extend_size  = (hashcode_is_attached((Partial_Reveal_Object*)next_area_start))?GC_OBJECT_ALIGNMENT:0;
#endif

            if(obj_is_marked_in_vt((Partial_Reveal_Object*)next_area_start)){
                POINTER_SIZE_INT obj_size = ALIGN_UP_TO_KILO(vm_object_size((Partial_Reveal_Object*)next_area_start) + hash_extend_size);
                *iterate_index = (unsigned int)((next_area_start + obj_size - (POINTER_SIZE_INT)lspace->heap_start) >> BIT_SHIFT_TO_KILO);
                return (Partial_Reveal_Object*)next_area_start;
            //If this is a dead object, go on to find  a living one.
            }else{
                POINTER_SIZE_INT obj_size = ALIGN_UP_TO_KILO(vm_object_size((Partial_Reveal_Object*)next_area_start)+ hash_extend_size);
                next_area_start += obj_size;
            }
        }else{
            reach_heap_end = 1;
        } 
    }
    return NULL;

}

inline Partial_Reveal_Object* lspace_get_first_marked_object(Lspace* lspace, unsigned int* mark_bit_idx)
{
    return lspace_get_next_marked_object(lspace, mark_bit_idx);
}

void lspace_fix_after_copy_nursery(Collector* collector, Lspace* lspace);

void lspace_fix_repointed_refs(Collector* collector, Lspace* lspace);

POINTER_SIZE_INT lspace_get_failure_size(Lspace* lspace);

inline Partial_Reveal_Object* lspace_get_next_marked_object_by_oi( Lspace* lspace, unsigned int* iterate_index)
{
    POINTER_SIZE_INT next_area_start = (POINTER_SIZE_INT)lspace->heap_start + (*iterate_index) * KB;
    BOOLEAN reach_heap_end = 0;
    unsigned int hash_extend_size = 0;
   
while(!reach_heap_end){        
   //FIXME: This while shoudl be if, try it!
        while((next_area_start<(POINTER_SIZE_INT)lspace->heap_end) && !*((POINTER_SIZE_INT*)next_area_start)){
            assert(((Free_Area*)next_area_start)->size);
            next_area_start += ((Free_Area*)next_area_start)->size;    
        }
        if(next_area_start < (POINTER_SIZE_INT)lspace->heap_end){
            //If there is a living object at this addr, return it, and update iterate_index

#ifdef USE_32BITS_HASHCODE
            hash_extend_size  = (hashcode_is_attached((Partial_Reveal_Object*)next_area_start))?GC_OBJECT_ALIGNMENT:0;
#endif

            if(obj_is_marked_in_oi((Partial_Reveal_Object*)next_area_start)){
                POINTER_SIZE_INT obj_size = ALIGN_UP_TO_KILO(vm_object_size((Partial_Reveal_Object*)next_area_start) + hash_extend_size);
                *iterate_index = (unsigned int)((next_area_start + obj_size - (POINTER_SIZE_INT)lspace->heap_start) >> BIT_SHIFT_TO_KILO);
                return (Partial_Reveal_Object*)next_area_start;
            //If this is a dead object, go on to find  a living one.
            }else{
                POINTER_SIZE_INT obj_size = ALIGN_UP_TO_KILO(vm_object_size((Partial_Reveal_Object*)next_area_start)+ hash_extend_size);
                next_area_start += obj_size;
            }
        }else{
            reach_heap_end = 1;
        } 
    }
    return NULL;

}

inline static Partial_Reveal_Object* lspace_get_first_marked_object_by_oi(Lspace* lspace, unsigned int* mark_bit_idx)
{
    return lspace_get_next_marked_object_by_oi(lspace, mark_bit_idx);
}

void lspace_reset_for_sweep(Lspace* lspace);
void lspace_sweep(Lspace* lspace);


#endif /*_LSPACE_H_ */
